package com.franklin.java.inspection.sql;

import com.franklin.java.api.annotation.BaseAnnotation;
import com.franklin.java.api.psi.data.PsiCallingElement;
import com.franklin.java.completion.map.MapDataCompletionContributor;
import com.franklin.java.utils.PsiUtils;
import com.franklin.java.utils.ThreadUtils;
import com.franklin.plugins.common.collection.PrettyArrayList;
import com.franklin.plugins.common.utils.DataCheckUtils;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProcess;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInspection.*;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.psi.*;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
 * @author Franklin
 * @since 2021/9/9 11:09
 */
public class SqlErrorInspection extends AbstractBaseJavaLocalInspectionTool {

    public SqlErrorInspection() {
        BaseAnnotation.initialize();
    }

    @Override
    @Nullable
    public ProblemDescriptor[] checkMethod(@NotNull PsiMethod method, @NotNull InspectionManager manager, boolean isOnTheFly) {
        PsiCodeBlock body = method.getBody();
        if (Objects.isNull(body)) {
            return null;
        }
        List<PsiElement> errorKeyList = new PrettyArrayList<>();
        String[] lines = body.getText().split("\n");
        Multimap<PsiElement,PsiElement> multimap = ArrayListMultimap.create();
        int length = 0;
        for (String line : lines) {
            if (line.trim().startsWith("/")) {
                length = length + line.length() + 1;
                continue;
            }
            String noWhiteLine = StringUtils.deleteWhitespace(line);
            if (noWhiteLine.contains("get(\"")) {
                int offset = length + line.indexOf("get(");
                PsiElement psiElement = body.findElementAt(offset);
                PsiElement keyElement = body.findElementAt(offset + "get(".length());
                if (Objects.nonNull(psiElement) && Objects.nonNull(keyElement)) {
                    multimap.put(psiElement,keyElement);
                }
            }
            length = length + line.length() + 1;
        }

        Set<PsiElement> collectionElements = multimap.keySet();
        CountDownLatch countDownLatch = new CountDownLatch(collectionElements.size());
        for (PsiElement collectionElement : collectionElements) {
            Collection<PsiElement> keyElements = multimap.get(collectionElement);
            PsiFile psiFile = collectionElement.getContainingFile();
            Document document = psiFile.getViewProvider().getDocument();
            if (Objects.nonNull(document)) {
                Editor[] editors = EditorFactory.getInstance().getEditors(document);
                CompletionParameters completionParameters = new CompletionParameters(
                    collectionElement,
                    psiFile,
                    CompletionType.BASIC,
                    PsiUtils.findStartOffsetInPsiFile(collectionElement),
                    0,
                    editors[0],
                    () -> false
                );
                if (DataCheckUtils.isNotEmpty(keyElements)) {
                    ThreadUtils.runLater(psiFile.getProject(), "Checking Map Data Error",() -> {
                        MapDataCompletionContributor mapDataCompletionContributor = MapDataCompletionContributor.getInstance();
                        mapDataCompletionContributor.init(completionParameters);
                        if (mapDataCompletionContributor.isCorrectCompletionParameter(completionParameters)) {
                            List<LookupElement> lookupElements = mapDataCompletionContributor.getLookupElements(completionParameters);
                            Set<String> keySet = lookupElements.stream()
                                .map(LookupElement::getLookupString)
                                .collect(Collectors.toSet());
                            keyElements.forEach(keyElement -> {
                                if (!keySet.contains(keyElement.getText())){
                                    errorKeyList.add(keyElement);
                                }
                            });
                        }
                        countDownLatch.countDown();
                    });
                }
            }
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        ProblemDescriptor[] problemDescriptors = new ProblemDescriptor[errorKeyList.size()];

        for (int i = 0; i < errorKeyList.size(); i++) {
            PsiElement psiElement = errorKeyList.get(i);
            problemDescriptors[i] = manager.createProblemDescriptor(
                psiElement,
                String.format("This %s does not exist, please check your sql", psiElement.getText()),
                isOnTheFly,
                LocalQuickFix.EMPTY_ARRAY,
                ProblemHighlightType.GENERIC_ERROR_OR_WARNING
            );
        }

        return problemDescriptors;
    }

    @Override
    public @Nls(capitalization = Nls.Capitalization.Sentence)
    @NotNull
    String getDisplayName() {
        return "Column not exist error";
    }


}
